package com.xmxsuperstar.ant.task;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.Scanner;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;

import com.xmxsuperstar.ant.util.Native2AsciiUtils;

public class ConfTask extends Task {
	private static Pattern pattern = Pattern.compile("\\$!?\\{(.+?)\\}");

	private String vm;
	private String destPath;
	private String propPath;

	public void execute() {
		if (check()) {
			merge();
		} else {
			throw new BuildException(propPath + " not well prepared");
		}
	}

	private boolean check() {
		boolean checkSuccessed = true;
		try {
			Scanner in = new Scanner(new File(vm));
			File propFile = new File(propPath);
			if (!propFile.exists()) {
				propFile.createNewFile();
			}
			FileInputStream fis = new FileInputStream(propFile);
			Properties prop = new Properties();
			prop.load(fis);

			StringBuilder sb = new StringBuilder();
			while (in.hasNextLine()) {
				String line = in.nextLine().trim();
				sb.append(line);
			}
			Matcher m = pattern.matcher(sb);
			while (m.find()) {
				String key = m.group(1).replace('_', '.');
				if (!prop.containsKey(key)
						|| (((String) prop.get(key)).trim().length() == 0)) {
					checkSuccessed = false;
					log(key + " needed for " + vm);
					prop.put(key,
							"########Set Your Custome Value Here!########");
				}
			}
			if (!checkSuccessed) {
				storeNewProperties(prop);
			}
		} catch (Exception e) {
			throw new BuildException(e);
		}
		return checkSuccessed;
	}

	private void storeNewProperties(Properties prop) {
		try {
			BufferedWriter bw = new BufferedWriter(new PrintWriter(new File(
					propPath)));
			List<String> keyList = new ArrayList<String>();
			Set<Object> keySet = prop.keySet();
			for (Object key : keySet) {
				keyList.add((String) key);
			}
			Collections.sort(keyList);

			for (String key : keyList) {
				String val = (String) prop.get(key);
				key = Native2AsciiUtils.native2Ascii(key);
				val = Native2AsciiUtils.native2Ascii(val);
				if (val.equals("########Set Your Custome Value Here!########")) {
					bw.write("#");
				}
				bw.write(key + "=" + val);
				bw.newLine();
			}
			bw.flush();
		} catch (Exception e) {
			throw new BuildException(e);
		}
	}

	private void merge() {
		VelocityEngine ve = new VelocityEngine();
		File vmFile = new File(vm);
		ve.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, vmFile.getParent());
		ve.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM_CLASS,
				"org.apache.velocity.runtime.log.NullLogSystem");
		try {
			ve.init();
			Template t = ve.getTemplate(vmFile.getName());
			VelocityContext context = new VelocityContext();

			FileInputStream fis = new FileInputStream(propPath);

			Properties prop = new Properties();
			prop.load(fis);
			Enumeration<Object> enu = prop.keys();
			while (enu.hasMoreElements()) {
				String key = enu.nextElement().toString();
				String value = prop.get(key).toString();
				if (destPath.endsWith(".properties")) {
					value = Native2AsciiUtils.native2Ascii(value);
				}
				context.put(key.replace('.', '_'), value);
			}

			BufferedWriter writer = new BufferedWriter(new FileWriter(destPath));
			t.merge(context, writer);
			writer.flush();
			writer.close();
			log("Generated config file: " + destPath + " from template: " + vm
					+ " using properties: " + propPath, Project.MSG_INFO);
		} catch (Exception e) {
			throw new BuildException(e);
		}
	}

	public void setVm(String vm) {
		this.vm = vm;
	}

	public void setDestPath(String destPath) {
		this.destPath = destPath;
	}

	public void setPropPath(String propPath) {
		this.propPath = propPath;
	}

}
